package com.gitee.aurora.engine.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.aurora.core.rule.InvokeRules;
import com.gitee.aurora.core.utils.ResultVO;
import com.gitee.aurora.core.utils.ResultVOUtils;
import com.gitee.aurora.core.utils.RulesResult;
import com.gitee.aurora.engine.dao.SjzxCheckDao;

import com.gitee.aurora.engine.po.LogResult;
import com.gitee.aurora.engine.po.RulesLogResult;
import com.gitee.aurora.engine.service.SjzxCheckService;
import com.gitee.aurora.engine.vo.DataCheckVo;
import com.gitee.aurora.engine.vo.DataResultVo;
import com.gitee.aurora.engine.vo.SyncBaqDataVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * author: jiangfuxiao
 * Date:2022/9/2 11:34
 */
@Service("sjzxCheckServiceImpl")
public class SjzxCheckServiceImpl implements SjzxCheckService {

    @Resource(name = "sjzxCheckDaoImpl")
    private SjzxCheckDao sjzxCheckDao;

    @Resource
    private RestTemplate restTemplate;

    @Autowired
    private InvokeRules invokeRules;

    @Value("${deptCode}")
    private String deptCode;

    @Value("${http.baqcx}")
    private String baqUrl;

    @Value("${http.zfjdcx}")
    private String zfjdUrl;



    @Override
    public ResultVO  queryTableData(DataCheckVo checkVo) {

        //创建统一的任务执行时间
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        checkVo.setInsertDate(format.format(new Date()));

        return countDataCheck(checkVo);

    }


    //办案区->数据中心->监督数量统计分析方法
    private ResultVO  countDataCheck(DataCheckVo checkVo){
        //结果集
        Map<String, Object> result = new HashMap<>();
        List<LinkedHashMap<String, Object>> resultTab;

        //获取办案区和数据中心以及监督数据
        Map<String, Object> map = requestDataAll(checkVo);


        List<LinkedHashMap<String, Object>> zfjdData = (List<LinkedHashMap<String, Object>>)map.get("zfjdData");
        List<LinkedCaseInsensitiveMap<Object>> sjzxData = (List<LinkedCaseInsensitiveMap<Object>>)map.get("sjzxData");
        List<LinkedHashMap<String, Object>> baqData = (List<LinkedHashMap<String, Object>>)map.get("baqData");

        //核验日志插入方法
        columnCheckInsertLog(sjzxData,checkVo);

        //初始化返回数据
        result.put("status",0);
        result.put("dataSjzx",null);
        result.put("dataZfjd",null);
        
        //各组件返回的数据 数量分析对比
        if (baqData.size() > sjzxData.size()){
            //过滤出数据中心需要同步的结果集返回给前端
            resultTab = baqData.stream().filter(
                    (eamData) ->!sjzxData.stream().map(value -> value.get("S_ID")).collect(Collectors.toList()).contains(eamData.get("sId"))
            ).collect(Collectors.toList());
            result.put("status",1);
            result.put("dataSjzx",resultTab);
        }
        if (sjzxData.size()>zfjdData.size() ){
            //过滤出监督需要同步的结果集返回给前端
            List<LinkedCaseInsensitiveMap<Object>>  resultZfjdTab = sjzxData.stream().filter(
                    (eamData) ->!zfjdData.stream().map(value -> value.get("sId")).collect(Collectors.toList()).contains(eamData.get("S_ID"))
            ).collect(Collectors.toList());

            result.put("status",1);
            result.put("dataZfjd",resultZfjdTab);
        }

        result.put("zfjdCount",zfjdData.size());
        result.put("sjzxCount",sjzxData.size());
        result.put("baqCount",baqData.size());

        return ResultVOUtils.success(result);
    }



    @Override
    public ResultVO queryTableDataSjzx(DataCheckVo checkVo) {

        List<LinkedHashMap<String, Object>> baqDataAll = new ArrayList<>();
        Map<String, List<Object>> groupBaqData = new HashMap<>();
        List<String> baqIps = checkVo.getBaqIp();
        for (String baqIp:baqIps) {
            String tableName = checkVo.getTableName();
            String sql = sjzxCheckDao.querySqlStr(baqIp,tableName);
            checkVo.setSqlStr(sql);
            List<LinkedHashMap<String, Object>> baqData = getBAQData(checkVo,baqIp);
            groupBaqData.put(baqIp,baqData.stream().map(value -> String.valueOf(value.get("sId"))).collect(Collectors.toList()));
            if (!CollectionUtils.isEmpty(baqData)){
                baqDataAll.addAll(baqData);
            }
        }

        List<LinkedCaseInsensitiveMap<Object>> sjzxData = new ArrayList<>();


        groupBaqData.forEach((k,v) ->{
            checkVo.setIds(v);
            List<LinkedCaseInsensitiveMap<Object>> sjzxDataId = (List<LinkedCaseInsensitiveMap<Object>>)getSJZXData(checkVo);
            if (!CollectionUtils.isEmpty(sjzxDataId)) {
                for (LinkedCaseInsensitiveMap<Object> map : sjzxDataId) {
                    map.put("ip", k);
                }
                sjzxData.addAll(sjzxDataId);
            }
        });


        //核验日志插入方法
        columnCheckInsertLog(sjzxData,checkVo);

        //过滤出需要同步的结果集返回
        groupBaqData.forEach((k,v) -> {
                groupBaqData.put(k,v.stream().filter(
                        (eamData) -> !sjzxData.stream().map(value -> value.get("S_ID")).collect(Collectors.toList()).contains(String.valueOf(eamData))
                ).collect(Collectors.toList())
            );
        });
        DataResultVo result = new DataResultVo();
        result.setSjzxIp(String.valueOf(checkVo.getSjzxIp().get(0)));
        result.setGroupBaqData(groupBaqData);
        result.setSjzxIds(sjzxData.stream().map(value -> value.get("S_ID")).collect(Collectors.toList()));
        result.setSjzxCount(sjzxData.size());
        result.setBaqCount(baqDataAll.size());
        return ResultVOUtils.success(result);
    }

    @Override
    public void taskWorkScheduled(DataCheckVo vo) {

        //创建统一的任务执行时间
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        vo.setInsertDate(format.format(new Date()));

        sjzxCheckDao.queryTable().stream().forEach(v -> {
            Map<String,String> var = (Map<String,String>)v;
            vo.setTableName(var.get("tabCode"));
            automaticCheck(vo);
        });

    }

    //自动核验方法实现
    public void automaticCheck(DataCheckVo checkVo){

        //获取办案区和数据中心以及监督数据
        Map<String, Object> map = requestDataAll(checkVo);
        List<LinkedHashMap<String, Object>> zfjdData = (List<LinkedHashMap<String, Object>>)map.get("zfjdData");
        List<LinkedCaseInsensitiveMap<Object>> sjzxData = (List<LinkedCaseInsensitiveMap<Object>>)map.get("sjzxData");
        Map<String, List<LinkedHashMap<String, Object>>> groupBaqData = (Map<String, List<LinkedHashMap<String, Object>>>)map.get("groupBaqData");

        //需要存入数据库的数据集合
        List<LogResult> dataList = new ArrayList<>();

        //核验日志插入方法
        columnCheckInsertLog(sjzxData, checkVo);

        groupBaqData.forEach((baqIp,baqNum) ->{
            LogResult logResult = new LogResult();

            List<LinkedHashMap<String, Object>> sjzxNum = baqNum.stream().filter(
                    (eamData) -> sjzxData.stream().map(value -> value.get("S_ID")).collect(Collectors.toList()).contains(eamData.get("sId"))
            ).collect(Collectors.toList());

            int zfjdNum = sjzxNum.stream().filter(
                    (eamData) ->zfjdData.stream().map(value -> value.get("sId")).collect(Collectors.toList()).contains(eamData.get("sId"))
            ).collect(Collectors.toList()).size();

            logResult.setId(UUID.randomUUID().toString());
            logResult.setSjzxNum(String.valueOf(sjzxNum.size()));
            logResult.setZfjdNum(String.valueOf(zfjdNum));
            logResult.setBaqNum(String.valueOf(baqNum.size()));
            logResult.setSourceIp(baqIp);
            logResult.setInsertTime(checkVo.getInsertDate());
            logResult.setTableName(checkVo.getTableName());
            dataList.add(logResult);
        });
        List<LogResult> insertData = dataList.stream().filter((data) -> !"0".equals(data.getBaqNum())).collect(Collectors.toList());
        sjzxCheckDao.saveLogResult(insertData);

    }


    public Map<String,Object> requestDataAll(DataCheckVo checkVo) {

        List<LinkedHashMap<String, Object>> baqDataAll = new ArrayList<>();
        Map<String, List<LinkedHashMap<String, Object>>> groupBaqData = new HashMap<>();

        List<String> baqIps = checkVo.getBaqIp();
        for (String baqIp:baqIps) {
            String tableName = checkVo.getTableName();
            String sql = sjzxCheckDao.querySqlStr(baqIp,tableName);
            checkVo.setSqlStr(sql);
            List<LinkedHashMap<String, Object>> baqData = getBAQData(checkVo,baqIp);
            groupBaqData.put(baqIp,baqData);
            if (!CollectionUtils.isEmpty(baqData)){
                baqDataAll.addAll(baqData);
            }
        }

        List<LinkedCaseInsensitiveMap<Object>> sjzxData = new ArrayList<>();
        List<Object> ids =new ArrayList<>();
        groupBaqData.forEach((k,v) ->{
            List<Object>  sIdList =v.stream().map(value -> value.get("sId")).collect(Collectors.toList());
            checkVo.setIds(sIdList);
            ids.addAll(sIdList);
            List<LinkedCaseInsensitiveMap<Object>> sjzxDataId = (List<LinkedCaseInsensitiveMap<Object>>)getSJZXData(checkVo);
            if (!CollectionUtils.isEmpty(sjzxDataId)) {
                for (LinkedCaseInsensitiveMap<Object> map : sjzxDataId) {
                    map.put("ip", k);
                }
                sjzxData.addAll(sjzxDataId);
            }
        });

        checkVo.setIds(ids);
        List<LinkedHashMap<String, Object>> zfjdData = getZFJDData(checkVo);

        Map<String,Object> map = new HashMap<>();
        map.put("baqData",baqDataAll);
        map.put("sjzxData",sjzxData);
        map.put("zfjdData",zfjdData);
        map.put("groupBaqData",groupBaqData);
        return map;
    }


    //数据中心校验字段插入日志
    private void columnCheckInsertLog(List<LinkedCaseInsensitiveMap<Object>> sjzxData ,DataCheckVo checkVo){
        //校验字段插入日志
        sjzxData.forEach(v ->{
            Map<String, String> ruleMap = getRuleMap(checkVo.getTableName(), deptCode);
            if (!CollectionUtils.isEmpty(ruleMap)) {
                List<RulesResult> rulesResults = invokeRules.doInvoke(v, ruleMap);
                List<RulesLogResult> insertData = new ArrayList<>();
                rulesResults.forEach(v1 -> {
                    RulesLogResult rulesLogResult = new RulesLogResult();
                    rulesLogResult.setId(UUID.randomUUID().toString());
                    BeanUtils.copyProperties(v1, rulesLogResult);
                    rulesLogResult.setTableName(checkVo.getTableName());
                    rulesLogResult.setTableId(Objects.requireNonNull(v.get("S_ID")).toString());
                    rulesLogResult.setDeptCode(Objects.requireNonNull(v.get("ip")).toString());
                    rulesLogResult.setInsertTime(checkVo.getInsertDate());
                    insertData.add(rulesLogResult);
                });
                //插入主服务的日志表
                sjzxCheckDao.saveRulesLogResult(insertData);
            }
        });
    }

    private Map<String, String> getRuleMap(String tableName, String deptCode) {
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> map = sjzxCheckDao.getRule(tableName, deptCode);
        try {
            Object rule = map.get("RULE");
            return mapper.readValue(rule.toString(), Map.class);
        }catch (Exception e){
            e.getStackTrace();
        }
        return null;
    }


    private List<LinkedHashMap<String, Object>> getBAQData(DataCheckVo checkVo,String ip) {
        HttpHeaders headers = new HttpHeaders();
        HttpEntity<DataCheckVo> httpEntity = new HttpEntity<>(checkVo, headers);
        //请求办案区数据
        try {
            String baq = baqUrl.replace("ip", ip);
            ResponseEntity<ResultVO> responseBaq = restTemplate.exchange(baq, HttpMethod.POST, httpEntity, ResultVO.class);
            return (List<LinkedHashMap<String, Object>>) responseBaq.getBody().getData();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private List getSJZXData(DataCheckVo checkVo) {
        return sjzxCheckDao.queryTableDataByIds(checkVo);
    }

    private List<LinkedHashMap<String, Object>> getZFJDData(DataCheckVo checkVo) {
        HttpHeaders headers = new HttpHeaders();
        HttpEntity<DataCheckVo> httpEntity = new HttpEntity<>(checkVo, headers);
        if (StringUtils.isNotBlank(checkVo.getZfjdIp())){
            String zfjd = zfjdUrl.replace("ip",checkVo.getZfjdIp());
            ResponseEntity<ResultVO> responseZfjd = restTemplate.exchange(zfjd, HttpMethod.POST, httpEntity, ResultVO.class);
            return (List<LinkedHashMap<String, Object>>) responseZfjd.getBody().getData();
        }
        return null;
    }



    @Override
    public ResultVO insertData(DataCheckVo vo) {
        return  sjzxCheckDao.insertData(vo);
    }

    @Override
    public ResultVO syncBaqData(SyncBaqDataVo vo) {
        ObjectMapper mapper = new ObjectMapper();
        vo.getGroupBaqData().forEach((baqIp,data) -> {
            DataCheckVo checkVo = new DataCheckVo();
            checkVo.setTableName(vo.getTableName());
            checkVo.setBaqIp(Arrays.asList(baqIp));
            checkVo.setIds(data);
            try {
                String listStr = mapper.writeValueAsString(getBAQData(checkVo,checkVo.getBaqIp().get(0)));
                checkVo.setListStr(listStr);
                insertData(checkVo);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        });
        return  ResultVOUtils.success(deptCode+"： 数据中心插入数据成功");
    }

    @Override
    public ResultVO  queryTableDataByIds(DataCheckVo checkVo) {
        List list = sjzxCheckDao.queryTableDataByIds(checkVo);
        return  ResultVOUtils.success(list);
    }

    @Override
    public ResultVO getDBTableField(String tableName) {
        return sjzxCheckDao.getDBTableField(tableName);
    }

}
